/**
 * @file I2C_lib.c
 *
 * Funciones del control del Bus I2C
 *
 * @version  1.00a
 * @date     13/5/2014
 * @author   Pirx
 *
 */

/***************************** Include Files *********************************/

#include <p24F32KA302.h>
#include "I2C_lib.h"
#include "Hardware.h"

/************************** Constant Definitions ******************************/

#define	I2C_TIME_ACK		160					//ms

/**************************** Type Definitions ******************************/

/* None */

/***************************** Variable Definitions *************************/

static StrI2C	I2Cpax[1];						// Variable Global para el control del I2C *** SOLO 1 I2C


/************************** Function Prototypes *****************************/

/* None */


/******************************************************************************/
/**
* Inicializa el Bus I2C
*
* @param	dev  Bus I2C
* @return 	TRUE = Success
* @note		None
*
******************************************************************************/
extern UINT8 I2C_Init(UINT8 dev)
{
	I2Cpax[dev]._row = TRUE;
	I2Cpax[dev]._err = FALSE;
	I2Cpax[dev]._inc = FALSE;
	I2Cpax[dev].Estado = iIdle;
	I2Cpax[dev].tmrAck = 0;
	I2Cpax[dev].tmrInh = 0;
	I2Cpax[dev].pTx = 0;
	I2Cpax[dev].pRx = 0;
	I2Cpax[dev].RxLen = 0;
	I2Cpax[dev].TxLen = 0;
	I2Cpax[dev].Nsel = 0;				// Primer Elemento del Bus
	I2Cpax[dev].Ndev = dev;
	I2Cpax[dev].Device = 0;

	return TRUE;
}


/******************************************************************************/
/**
* Control de Timer del Bus I2C
*
* @param	dev  Bus I2C
* @return 	TRUE = Algun Timer Expirado
* @note		None
*
******************************************************************************/
extern UINT8 I2C_Timer(UINT8 dev)
{
	UINT8 _ret = FALSE;

	// Timer de Seguridad
	if (I2Cpax[dev].tmrAck > 0){
		I2Cpax[dev].tmrAck--;
		if (I2Cpax[dev].tmrAck == 0){
			// Reset I2C			!! Falta Pendiente
			_ret = TRUE;
		}
	}

	// Timer de Inhibicion
	if (I2Cpax[dev].tmrInh > 0){
		I2Cpax[dev].tmrInh--;
		if (I2Cpax[dev].tmrInh == 0){
			_ret = TRUE;
		}
	}

	return (_ret)? TRUE : FALSE;
}


/******************************************************************************/
/**
* Obtiene la direccion de la Estructura de Control del I2C
*
* @param	dev  Bus I2C
* @return 	Direccion del componente StrI2C
* @note		None
*
******************************************************************************/
extern StrI2C* I2C_get_adr(UINT8 dev)
{
	return (StrI2C*) &I2Cpax[dev];
}


/******************************************************************************/
/**
* Obtiene el Estado Final de la Maquina
*
* @param	dev  Bus I2C
* @return 	Valor de _end
* @note		None
*
******************************************************************************/
extern UINT8 I2C_get_end(UINT8 dev)
{
	if (I2Cpax[dev].Estado == iEnd){
		return TRUE;
	} else {
		return FALSE;
	} 
}


/******************************************************************************/
/**
* Obtiene el Estado ocupado de la Maquina
*
* @param	dev  Bus I2C
* @return 	TRUE si no esta Idle
* @note		None
*
******************************************************************************/
extern UINT8 I2C_get_bsy(UINT8 dev)
{
	if (I2Cpax[dev].Estado > iIdle){
		return TRUE;
	} else {
		return FALSE;
	} 
}


/******************************************************************************/
/**
* Obtiene el valor de la variable _inc
*
* @param	dev  Bus I2C
* @return 	Valor de _inc
* @note		None
*
******************************************************************************/
extern UINT8 I2C_get_inc(UINT8 dev)
{
	return I2Cpax[dev]._inc;
}


/******************************************************************************/
/**
* Obtiene el valor de la variable Nsel
*
* @param	dev  Bus I2C
* @return 	Valor de Nsel
* @note		None
*
******************************************************************************/
extern UINT8 I2C_get_Nsel(UINT8 dev)
{
	return I2Cpax[dev].Nsel;
}


/******************************************************************************/
/**
* Obtiene el valor de tmrInh
*
* @param	dev  Bus I2C
* @return 	Valor del timer
* @note		None
*
******************************************************************************/
extern UINT16 I2C_get_tmrInh(UINT8 dev)
{
	return I2Cpax[dev].tmrInh;
}


/******************************************************************************/
/**
* Limpia el estado iEnd de la maquina
*
* @param	dev  Bus I2C
* @return 	FALSE=Error / TRUE=Cambio
* @note		None
*
******************************************************************************/
extern UINT8 I2C_clear_end(UINT8 dev)
{
	if (I2Cpax[dev].Estado == iEnd){
		I2Cpax[dev].Estado = iIdle;
		return TRUE;
	} else {
		return FALSE;
	}
}


/******************************************************************************/
/**
* Establece el Valor de _inc
*
* @param	dev  Bus I2C
* @param	dat Nuevo Valor
* @return 	TRUE si ha habido cambio
* @note		None
*
******************************************************************************/
extern UINT8 I2C_set_inc(UINT8 dev, UINT8 _dat)
{
	if (I2Cpax[dev]._inc != _dat){
		I2Cpax[dev]._inc = _dat;
		return TRUE;
	} else {
		return FALSE;
	}
}


/******************************************************************************/
/**
* Establece el Valor de Select
*
* @param	dev  Bus I2C
* @param	dat Nuevo Valor
* @return 	TRUE si ha habido cambio
* @note		None
*
******************************************************************************/
extern UINT8 I2C_set_Nsel(UINT8 dev, UINT8 sel)
{
	if (I2Cpax[dev].Nsel != sel){
		I2Cpax[dev].Nsel = sel;
		return TRUE;
	} else {
		return FALSE;
	}
}


/******************************************************************************/
/**
* Establece el Valor del Temporizador TimerInh
*
* @param	dev  Bus I2C
* @param	tim Nuevo Valor
* @return 	TRUE si ha habido cambio
* @note		None
*
******************************************************************************/
extern UINT8 I2C_set_tmrInh(UINT8 dev, UINT16 tim)
{
	I2Cpax[dev].tmrInh = tim;
	return TRUE;
}


/******************************************************************************/
/**
* Operacion de Lectura del I2C
* No bloquea la ejecucion del programa
*
* @param	Estructura del I2C
* @param	Bus I2C [1,2]	*de momento solo esta el 1
* @return	TRUE = Error
* @note		None
*
******************************************************************************/
extern UINT8 I2cTrans(StrI2C* i2c)
{
	UINT8 _ret;

	if (i2c->Estado == iIdle){
		i2c->_row = (i2c->TxLen == 0)? TRUE : FALSE;		// Si no hay datos que escribir es una lectura directamente
		i2c->_err = FALSE;									// No Error
		i2c->pTx = 0;										// Iniciar Punteros
		i2c->pRx = 0;
		if ( (i2c->_row == TRUE) && (i2c->RxLen == 0) ){
			_ret = TRUE;									// Error -> Transmision Vacia
		} else {
			i2c->Estado = iStart;
			i2c->tmrAck = (UINT16)((1000*(UINT32)I2C_TIME_ACK)/(UINT32)TIMEBASE);
			_ret = FALSE;
			if (i2c->Ndev == I2C_1){
				I2C1CONbits.SEN = TRUE;						// -> Inicio I2C1
			} else {
				I2C2CONbits.SEN = TRUE;						// -> Inicio I2C2
			}
		}
	} else {
		_ret = TRUE;
	}

	return (_ret)? TRUE : FALSE;
}


/******************************************************************************/
/**
* Rutina de Atencion del Bus I2C
* Gestiona la maquina de estado de las transmisiones en el bus
*
* @param	None
* @return	None
* @note		None
*
******************************************************************************/
void __attribute__((interrupt,no_auto_psv)) _MI2C1Interrupt(void)
{
	UINT8	_err = FALSE;										// No Error

	IFS1bits.MI2C1IF = FALSE;									// Limpio Bandera

	if ((I2C1STATbits.IWCOL == FALSE) && (I2C1STATbits.IWCOL == FALSE)){
		switch (I2Cpax[I2C_1].Estado){
			case iStart:
				if (I2C1STATbits.S == TRUE){
					//if ( (I2Cpax._row == TRUE) && (I2Cpax.TxLen == 0) ){
					if (I2Cpax[I2C_1].TxLen == 0){
						I2C1TRN = (I2Cpax[I2C_1].Device | 0x1);
						I2Cpax[I2C_1].Estado = iDevRx;
					} else {
						I2C1TRN = I2Cpax[I2C_1].Device;
						I2Cpax[I2C_1].Estado = iDevTx;
					}				
				} else {
					_err = TRUE;
				}
				break;
			case iDevTx:
				if (I2C1STATbits.ACKSTAT == FALSE){				// Comprobar ACK del Dispositivo
					if (I2Cpax[I2C_1].TxLen > 0){
						I2C1TRN = I2Cpax[I2C_1].TxBuf[0];		// Transmitir Primer Dato del Buffer
						I2Cpax[I2C_1].pTx++;
						I2Cpax[I2C_1].Estado = iTx;
					} else {
						// No tiene sentido Estar Aqui
						_err = TRUE;
					}
				} else {
					_err = TRUE;								// Error ... no contesta
				}
				break;
			case iTx:
				if (I2C1STATbits.ACKSTAT == FALSE){				// Comprobar ACK del Dispositivo
					if (I2Cpax[I2C_1].pTx != I2Cpax[I2C_1].TxLen){
						I2C1TRN = I2Cpax[I2C_1].TxBuf[I2Cpax[I2C_1].pTx];
						I2Cpax[I2C_1].pTx++;
					} else {
						// Fin de la Transmision
						if (I2Cpax[I2C_1].RxLen > 0){
							I2Cpax[I2C_1]._row = TRUE;			// Marcar Lectura
							I2C1CONbits.RSEN = TRUE;			// Restart
							I2Cpax[I2C_1].Estado = iRestart;
						} else {
							I2C1CONbits.PEN = TRUE;				// Stop
							I2Cpax[I2C_1].Estado = iStop;		
						}
					}
				} else {
					_err = TRUE;								// Error ... no contesta
				}
				break;
			case iRestart:
				I2C1TRN = (I2Cpax[I2C_1].Device | 0x1);
				I2Cpax[I2C_1].pRx = 0;
				I2Cpax[I2C_1].Estado = iDevRx;
				break;
			case iDevRx:
				if ( (I2Cpax[I2C_1].RxLen > 0) && (I2Cpax[I2C_1].pRx != I2Cpax[I2C_1].RxLen) ){
					I2C1CONbits.RCEN = TRUE;
					I2Cpax[I2C_1].Estado = iRx;					// Comenzar Recepcion
				} else {
					I2C1CONbits.PEN = TRUE;						// Stop
					I2Cpax[I2C_1].Estado = iStop;
				}
				break;
			case iRx:
				if (I2C1STATbits.RBF == TRUE){
					I2Cpax[I2C_1].RxBuf[I2Cpax[I2C_1].pRx] = I2C1RCV;		// Dato Recibido
					I2Cpax[I2C_1].pRx++;
					I2C1CONbits.ACKDT = (I2Cpax[I2C_1].pRx == I2Cpax[I2C_1].RxLen)? TRUE : FALSE;
					I2C1CONbits.ACKEN = TRUE;
					I2Cpax[I2C_1].Estado = iAck;
				} else {
					_err = TRUE;
				}
				break;
			case iAck:
				if (I2Cpax[I2C_1].pRx < I2Cpax[I2C_1].RxLen){
					I2C1CONbits.RCEN = TRUE;
					I2Cpax[I2C_1].Estado = iRx;
				} else {
					I2C1CONbits.PEN = TRUE;						// Stop
					I2Cpax[I2C_1].Estado = iStop;
				}
				break;
			case iStop:
				if (I2C1STATbits.P == TRUE){
					I2Cpax[I2C_1].Estado = iEnd;
					I2Cpax[I2C_1].tmrAck = 0;					// 
				} else {
					_err = TRUE;
				}
				break;
			default:
				_err = TRUE;
				break;
		}
	} else {
		_err = TRUE;
	}

	if (_err == TRUE){											// Procesar Fallo
		I2Cpax[I2C_1]._err = TRUE;								// Marcar Error I2C
		//I2Cpax[I2C_1].Estado = iEnd;
		I2C1CONbits.PEN = TRUE;									// Stop
		I2Cpax[I2C_1].Estado = iStop;
	}

}
